/**
  EG4Sim project, Anyang Normal University && IMP-CAS
  \class EG4Dipo
  \brief the model of the dipo magnet
  \author SUN Yazhou, asia.rabbit@163.com
  \since 2023-10-12
  \date 2023-10-12 last modified
  \copyright Copyright (c) 2023-2024 IMP-CAS with LGPLv3 LICENSE
*/

#include "EG4Dipo.h"
#include "G4Box.hh"
#include "G4Trap.hh"
#include "G4LogicalVolume.hh"
#include "G4PVPlacement.hh"
#include "G4TessellatedSolid.hh"
#include "G4QuadrangularFacet.hh"
#include "G4TriangularFacet.hh"
#include "G4NistManager.hh"
#include "G4Material.hh"
#include "G4SystemOfUnits.hh" // units imported from CLHEP library
#include "G4Box.hh"
#include "G4Tubs.hh"
#include "G4UserLimits.hh"
#include "G4RotationMatrix.hh"
#include "G4VisAttributes.hh"

using CLHEP::pi;

#define THICK_PILLAR // solid Pb impenetrable pillars (is not the real case)

EG4Dipo::EG4Dipo(){
  G4NistManager *nist = G4NistManager::Instance();
  G4Material *Fe = nist->FindOrBuildMaterial("G4_Fe");
  G4Material *Cu = nist->FindOrBuildMaterial("G4_Cu");
  G4Material *air = G4Material::GetMaterial("airLanzhou", true);
  G4Material *FR4 = G4Material::GetMaterial("FR4", true); // the PCB baseplate
#ifndef THICK_PILLAR
  G4Material *Al5052 = G4Material::GetMaterial("Al5052", true); // the supporting pillar
#else
  G4Material *Pb = nist->FindOrBuildMaterial("G4_Pb");
#endif

  auto blue = new G4VisAttributes(true, G4Colour(.5, .5, 1.)); // light blue
  // construct the mother volume to accommodate the dipo complex //
  G4Box *world = new G4Box("dipo", 1100.*mm, 950.*mm, 725.*mm);
  fLV = new G4LogicalVolume(world, air, "dipo");
  // set step limit in magnetic field
  fLV->SetUserLimits(new G4UserLimits(1.*m));

  //----- construct the iron frame -----//
  // left-right wall
  auto irxLV = new G4LogicalVolume(new G4Box("irx", 240., 950., 475.), Fe, "irx");
  irxLV->SetVisAttributes(blue);
  new G4PVPlacement(nullptr, G4ThreeVector(-860., 0., 0.), irxLV, "irxL",
    fLV, false, 0, true);
  new G4PVPlacement(nullptr, G4ThreeVector(860., 0., 0.), irxLV, "irxR",
    fLV, false, 1, true);
  // up-down wall
  auto iryLV = new G4LogicalVolume(new G4Box("iry", 620., 257.5, 475.), Fe, "iry");
  iryLV->SetVisAttributes(blue);
  new G4PVPlacement(nullptr, G4ThreeVector(0., 692.5, 0.), iryLV, "irU",
    fLV, false, 0, true);
  new G4PVPlacement(nullptr, G4ThreeVector(0., -692.5, 0.), iryLV, "iryD",
    fLV, false, 0, true);
  // the central heads - a trapezoid (trap, above) + a box (bh, blew)
  auto trapLV = new G4LogicalVolume(new G4Trap("head", 950., 20., 170., 50.), Fe, "trap");
  trapLV->SetVisAttributes(blue);
  auto bhLV = new G4LogicalVolume(new G4Box("bh", 85., 145., 475.), Fe, "bh");
  bhLV->SetVisAttributes(blue);
  // now we place the head fringes //
  new G4PVPlacement(nullptr, G4ThreeVector(285., -135., 0.), trapLV, "trapRD",
    fLV, false, 0, true);
  new G4PVPlacement(nullptr, G4ThreeVector(315., -290., 0.), bhLV, "bhRD",
    fLV, false, 0, true); // --------- right down
  new G4PVPlacement(new G4RotationMatrix(0., pi, 0.),
    G4ThreeVector(285., 135., 0.), trapLV, "trapRU", fLV, false, 1, true);
  new G4PVPlacement(nullptr, G4ThreeVector(315., 290., 0.), bhLV, "bhRU",
    fLV, false, 1, true); // --------- right up
  new G4PVPlacement(new G4RotationMatrix(pi, 0., 0.),
    G4ThreeVector(-285., 135., 0.), trapLV, "trapLU", fLV, false, 2, true);
  new G4PVPlacement(nullptr, G4ThreeVector(-315., 290., 0.), bhLV, "bhLU",
    fLV, false, 2, true); // --------- left up
  new G4PVPlacement(new G4RotationMatrix(pi, pi, 0.),
    G4ThreeVector(-285., -135., 0.), trapLV, "trapLD", fLV, false, 3, true);
  new G4PVPlacement(nullptr, G4ThreeVector(-315., -290., 0.), bhLV, "bhLD",
    fLV, false, 3, true); // --------- left down
  // the head core
  auto headLV = new G4LogicalVolume(new G4Box("head", 230., 150., 475.), Fe, "head");
  headLV->SetVisAttributes(blue);
  new G4PVPlacement(nullptr, G4ThreeVector(0., -285., 0.), headLV, "head",
    fLV, false, 0, true);
  new G4PVPlacement(nullptr, G4ThreeVector(0., 285., 0.), headLV, "head",
    fLV, false, 1, true);

  G4Box *fr4Plate = new G4Box("fr4Plate", 15.*mm, 215.*mm, 125.*mm);
  G4LogicalVolume *fr4PlateLV = new G4LogicalVolume(fr4Plate, FR4, "fr4Plate");
  fFR4PlatePVUp = new G4PVPlacement(nullptr, G4ThreeVector(710.*mm, 285.*mm, 600.*mm),
    fr4PlateLV, "FR4 Plate Up", fLV, false, 0, true);
  fFR4PlatePVDown = new G4PVPlacement(nullptr, G4ThreeVector(710.*mm, -325.*mm, 600.*mm),
    fr4PlateLV, "FR4 Plate Down", fLV, false, 1, true);
  fr4PlateLV->SetVisAttributes(new G4VisAttributes(G4Colour(.8, .8, 0.))); // yellow

  //----- Now the copper coil -----//
  G4Box *coilX = new G4Box("coilX", 610.*mm, 132.5*mm, 105.*mm);
  G4Box *coilZ = new G4Box("coilZ", 100.*mm, 132.5*mm, 475.*mm);
  G4LogicalVolume *coilXLV = new G4LogicalVolume(coilX, Cu, "coilX");
  G4LogicalVolume *coilZLV = new G4LogicalVolume(coilZ, Cu, "coilZ");
  coilXLV->SetVisAttributes(new G4VisAttributes(G4Colour(.816, .373, 0.016)));
  coilZLV->SetVisAttributes(new G4VisAttributes(G4Colour(.816, .373, 0.016)));
  // the downside coil //
  fCoilXD_d = new G4PVPlacement(nullptr, G4ThreeVector(0., -302.5*mm, 580.*mm),
    coilXLV, "coilXD_d", fLV, false, 0, true);
  fCoilXU_d = new G4PVPlacement(nullptr, G4ThreeVector(0., -302.5*mm, -580.*mm),
    coilXLV, "coilXU_d", fLV, false, 1, true);
  fCoilZL_d = new G4PVPlacement(nullptr, G4ThreeVector(-510.*mm, -302.5*mm, 0.),
    coilZLV, "coilZL_d", fLV, false, 0, true);
  fCoilZR_d = new G4PVPlacement(nullptr, G4ThreeVector(510.*mm, -302.5*mm, 0.),
    coilZLV, "coilZR_d", fLV, false, 1, true);
  // the upside coil //
  fCoilXD_u = new G4PVPlacement(nullptr, G4ThreeVector(0., 302.5*mm, 580.*mm),
    coilXLV, "coilXD_u", fLV, false, 0, true);
  fCoilXU_u = new G4PVPlacement(nullptr, G4ThreeVector(0., 302.5*mm, -580.*mm),
    coilXLV, "coilXU_u", fLV, false, 1, true);
  fCoilZL_u = new G4PVPlacement(nullptr, G4ThreeVector(-510.*mm, 302.5*mm, 0.),
    coilZLV, "coilZL_u", fLV, false, 0, true);
  fCoilZR_u = new G4PVPlacement(nullptr, G4ThreeVector(510.*mm, 302.5*mm, 0.),
    coilZLV, "coilZR_u", fLV, false, 1, true);

  //----- And the 4 coil pillars -----//
#ifndef THICK_PILLAR
  G4Tubs *pillar = new G4Tubs("pillar", 34.*mm, 40.*mm, 170.*mm, 0., 2.*pi);
  G4LogicalVolume *pillarLV = new G4LogicalVolume(pillar, Al5052, "pillar");
#else
  G4Tubs *pillar = new G4Tubs("pillar", 0., 40.*mm, 170.*mm, 0., 2.*pi);
  G4LogicalVolume *pillarLV = new G4LogicalVolume(pillar, Pb, "pillar");
#endif
  G4RotationMatrix *rotPillar = new G4RotationMatrix();
  rotPillar->rotateX(90.*deg);
  fPillar0 = new G4PVPlacement(rotPillar, G4ThreeVector(-510.*mm, 0., 423.*mm),
    pillarLV, "pillar0", fLV, false, 0, true);
  fPillar1 = new G4PVPlacement(rotPillar, G4ThreeVector(500.*mm, 0., 486.*mm),
    pillarLV, "pillar1", fLV, false, 1, true);
  fPillar2 = new G4PVPlacement(rotPillar, G4ThreeVector(-509.*mm, 0., -317.*mm),
    pillarLV, "pillar2", fLV, false, 2, true);
  fPillar3 = new G4PVPlacement(rotPillar, G4ThreeVector(515.*mm, 0., -351.*mm),
    pillarLV, "pillar3", fLV, false, 3, true);
  // OK, finally, completed //
} // end ctor

EG4Dipo::~EG4Dipo(){}
